package com.ayz.gateway.sdk.app;

import cn.hutool.core.thread.ThreadFactoryBuilder;

import com.ayz.gateway.api.request.ApplicationConfig;

import com.ayz.gateway.api.request.RegistryApplication;
import com.ayz.gateway.api.server.RegistryApplicationServerGrpc;
import com.ayz.gateway.client.GrpcClient;
import com.ayz.gateway.client.constants.RequestCodeConstants;
import com.ayz.gateway.client.exception.ExecuteRequestException;
import com.ayz.gateway.client.executor.GrpcRequestExecutor;
import com.ayz.gateway.common.constants.ApiData;
import com.ayz.gateway.common.constants.MethodData;
import com.ayz.gateway.sdk.app.cache.ApiDataHolder;
import com.ayz.gateway.sdk.app.config.AppSdkClientConfig;
import io.grpc.ManagedChannel;
import lombok.Data;

import java.io.Closeable;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName AppSdkClientController
 * @Description TODO：作为应用程序SDK的客户端 与网关注册中心交互
 * 主要功能: 完成应用程序中所有需要由网关管理的API接口信息的注册
 * @Author 快乐的星球
 * @Date 2023/5/22 22:32
 * @Version 1.0
 **/
@Data
public class AppSdkClientService implements Closeable{
    /**
     * SDK客户端的配置参数
     */
    private AppSdkClientConfig clientConfig;
    /**
     * 当前进程所有需要由网关管理的API接口信息的存储容器
     */
    private ApiDataHolder apiDataHolder;
    /**
     * 远程通信的客户端，凭借这个和服务端进行通信
     */
    private GrpcClient grpcClient;
    /**
     * 定时任务线程池
     */
    private final ScheduledExecutorService scheduledExecutorService= Executors.newSingleThreadScheduledExecutor(new ThreadFactoryBuilder().setDaemon(true)
            .setNamePrefix("ApiSdkClientServiceThread_").build());

    public AppSdkClientService(AppSdkClientConfig clientConfig, ApiDataHolder apiDataHolder, GrpcClient grpcClient) {
        this.clientConfig = clientConfig;
        this.apiDataHolder = apiDataHolder;
        this.grpcClient = grpcClient;
    }

    public void start(){
        //1: 完成GRPC客户端的请求执行器的注册
        grpcClient.registryRequestExecutors((requestExecutorRegistry)->{
            requestExecutorRegistry.registry(RequestCodeConstants.REGISTRY_APPLICATION_REQUEST,
                    new GrpcRequestExecutor() {
                        @Override
                        public void requestOneway(ManagedChannel managedChannel, Object request) throws ExecuteRequestException {
                            //1:准备好请求对象
                            RegistryApplication.RegistryApplicationRequest registryApplicationRequest=(RegistryApplication.RegistryApplicationRequest) request;
                            //2:创建存根
                            RegistryApplicationServerGrpc.RegistryApplicationServerFutureStub registryApplicationService = RegistryApplicationServerGrpc.newFutureStub(managedChannel);
                            registryApplicationService.registerApplication(registryApplicationRequest);
                        }
                    });
        });
        //2:完成GRPC客户端的启动
        grpcClient.start();
        //3:启动定时任务
        //3.1: AppSDK默认每隔10S种向网关注册中心上报ApiData
        scheduledExecutorService.scheduleAtFixedRate(()->{
            //1:先构造ApplicationConfigWrapper
            ApplicationConfig.ApplicationConfigWrapper applicationConfigWrapper = buildApplicationConfigWrapper();
            //2:构造API服务注册请求
            try {
                RegistryApplication.RegistryApplicationRequest registryApplicationRequest= RegistryApplication.RegistryApplicationRequest.newBuilder()
                        .setApplicationName(getClientConfig().getApplicationName()).setGatewayGroup(getClientConfig().getGatewayGroup()).setApplicationConfigWrapper(applicationConfigWrapper).build();
                //3:使用GRPC客户端将请求发送给服务器
                grpcClient.getCurrentConnection().requestOneway(RequestCodeConstants.REGISTRY_APPLICATION_REQUEST,registryApplicationRequest);
            }catch (Exception ex){
                ex.printStackTrace();
            }
        },10,getClientConfig().getRegistryApplicationTaskTime(), TimeUnit.MILLISECONDS);
    }

    private ApplicationConfig.ApplicationConfigWrapper buildApplicationConfigWrapper(){
        ApplicationConfig.ApplicationConfigWrapper.Builder builder = ApplicationConfig.ApplicationConfigWrapper.newBuilder();
        //1:填充版本号
        builder.setDataVersion(ApplicationConfig.DataVersion.newBuilder().setDataVersion(this.apiDataHolder.getDataVersion().getDataVersion())
                .setTimestamp(this.apiDataHolder.getDataVersion().getTimestamp()));
        //2:填充ApiData数组
        ConcurrentHashMap<String, ApiData> apiDataCache = this.apiDataHolder.getApiDataCache();
        if(!apiDataCache.isEmpty()){
            apiDataCache.forEach((interfaceName,apiData)->{
                ApplicationConfig.ApiData.Builder apiDataBuilder = ApplicationConfig.ApiData.newBuilder();
                apiDataBuilder.setApplicationName(apiData.getApplicationName());
                apiDataBuilder.setInterfaceName(apiData.getInterfaceName());
                apiDataBuilder.setRpcRegistryAddress(apiData.getRpcRegistryAddress());
                apiDataBuilder.setIsAsync(apiData.isAsync());
                apiDataBuilder.setTimeout(apiData.getTimeout());
                //对于每一个Api接口,为其构建所有的MethodData的详细信息
                List<MethodData> methodDataList = apiData.getMethodDataList();
                if(methodDataList!=null && !methodDataList.isEmpty()){
                    for (MethodData methodData : methodDataList) {
                        ApplicationConfig.MethodData.Builder methodDataBuilder = ApplicationConfig.MethodData.newBuilder();
                        methodDataBuilder.setUri(methodData.getUri());
                        methodDataBuilder.setMethodName(methodData.getMethodName());
                        methodDataBuilder.setIsAuth(methodData.isAuth());
                        methodDataBuilder.setParameterTypeName(methodData.getParameterTypeName());
                        apiDataBuilder.addMethodDataList(methodDataBuilder.build());
                    }
                }
                builder.addApiDataList(apiDataBuilder.build());
            });
        }
        return builder.build();
    }
    @Override
    public void close() throws IOException {
        try {
            grpcClient.close();
            scheduledExecutorService.shutdownNow();
        } catch (Exception e) {
        }
    }
}
